// Project: Salient
// http://salient.codeplex.com
// 
// Copyright 2010, Sky Sanders <sky at skysanders.net>
// Dual licensed under the MIT or GPL Version 2 licenses.
// http://salient.codeplex.com/license
// 
// Date: May 5 2010 

#region

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration.Provider;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Web;
using System.Web.Hosting;
using System.Web.Management;
using System.Web.Script.Serialization;
using Salient.Web.SQLite.Model;

#endregion

namespace Salient.Web.SQLite
{
    ///<summary>
    ///</summary>
    public static class ProviderUtility
    {
        ///<summary>
        ///</summary>
        public const int AppPerfCounterMemberFail = 72;

        ///<summary>
        ///</summary>
        public const int AppPerfCounterMemberSuccess = 71;

        ///<summary>
        ///</summary>
        public const int Infinite = Int32.MaxValue;

        ///<summary>
        ///</summary>
        public static readonly MethodInfo PerfCountersIncrementCounter;

        private static readonly MethodInfo SystemWebEventInternalInvoker;

        static ProviderUtility()
        {
            SystemWebEventInternalInvoker = typeof (WebBaseEvent).GetMethod("RaiseSystemEventInternal",
                                                                            BindingFlags.NonPublic | BindingFlags.Static,
                                                                            null,
                                                                            new[]
                                                                                {
                                                                                    typeof (string), typeof (object),
                                                                                    typeof (int), typeof (int),
                                                                                    typeof (Exception), typeof (string)
                                                                                },
                                                                            null);
            PerfCountersIncrementCounter =
                typeof (SiteMap).Assembly.GetType("System.Web.PerfCounters", true, false).GetMethod("IncrementCounter",
                                                                                                    BindingFlags.
                                                                                                        NonPublic |
                                                                                                    BindingFlags.Static);
        }

        ///<summary>
        ///</summary>
        ///<param name="source"></param>
        ///<param name="eventCode"></param>
        public static void RaiseSystemEvent(object source, int eventCode)
        {
            RaiseSystemEventInternal(null, source, eventCode, 0, null, null);
        }

        ///<summary>
        ///</summary>
        ///<param name="source"></param>
        ///<param name="eventCode"></param>
        ///<param name="eventDetailCode"></param>
        public static void RaiseSystemEvent(object source, int eventCode, int eventDetailCode)
        {
            RaiseSystemEventInternal(null, source, eventCode, eventDetailCode, null, null);
        }

        ///<summary>
        ///</summary>
        ///<param name="source"></param>
        ///<param name="eventCode"></param>
        ///<param name="nameToAuthenticate"></param>
        public static void RaiseSystemEvent(object source, int eventCode, string nameToAuthenticate)
        {
            RaiseSystemEventInternal(null, source, eventCode, 0, null, nameToAuthenticate);
        }

        ///<summary>
        ///</summary>
        ///<param name="source"></param>
        ///<param name="eventCode"></param>
        ///<param name="eventDetailCode"></param>
        ///<param name="exception"></param>
        public static void RaiseSystemEvent(object source, int eventCode, int eventDetailCode, Exception exception)
        {
            RaiseSystemEventInternal(null, source, eventCode, eventDetailCode, exception, null);
        }

        ///<summary>
        ///</summary>
        ///<param name="message"></param>
        ///<param name="source"></param>
        ///<param name="eventCode"></param>
        ///<param name="eventDetailCode"></param>
        ///<param name="exception"></param>
        public static void RaiseSystemEvent(string message, object source, int eventCode, int eventDetailCode,
                                            Exception exception)
        {
            RaiseSystemEventInternal(message, source, eventCode, eventDetailCode, exception, null);
        }

        private static void RaiseSystemEventInternal(string message, object source, int eventCode, int eventDetailCode,
                                                     Exception exception, string nameToAuthenticate)
        {
            SystemWebEventInternalInvoker.Invoke(null,
                                                 new[]
                                                     {
                                                         message, source, eventCode, eventDetailCode, exception,
                                                         nameToAuthenticate
                                                     });
        }


        ///<summary>
        ///</summary>
        ///<param name="input"></param>
        ///<returns></returns>
        public static NameValueCollection DeserializeNameValueCollection(string input)
        {
            NameValueCollection returnValue = new NameValueCollection();
            object[] tmp = (object[]) new JavaScriptSerializer().DeserializeObject(input);

            foreach (Dictionary<string, object> item in tmp)
            {
                returnValue.Add(item["Key"] as string, item["Value"] as string);
            }
            return returnValue;
        }

        ///<summary>
        ///</summary>
        ///<param name="col"></param>
        ///<returns></returns>
        public static string SerializeNameValueCollection(NameValueCollection col)
        {
            List<KeyValuePair<string, string>> tmp = new List<KeyValuePair<string, string>>();
            foreach (string key in col.Keys)
            {
                string[] values = col.GetValues(key);
                if (values != null)
                {
                    foreach (string value in values)
                    {
                        tmp.Add(new KeyValuePair<string, string>(key, value));
                    }
                }
            }
            return new JavaScriptSerializer().Serialize(tmp);
        }

        ///<summary>
        ///</summary>
        ///<returns></returns>
        public static DateTime Now()
        {
            return DateTime.Now.ToUniversalTime();
        }

        ///<summary>
        ///</summary>
        ///<param name="pageIndex"></param>
        ///<param name="pageSize"></param>
        ///<exception cref="ArgumentException"></exception>
        public static void VerifyPagingParameters(int pageIndex, int pageSize)
        {
            if (pageIndex < 0)
                throw new ArgumentException(SR.GetString(SR.PageIndexBad), "pageIndex");
            if (pageSize < 1)
                throw new ArgumentException(SR.GetString(SR.PageSizeBad), "pageSize");

            long upperBound = (long) pageIndex*pageSize + pageSize - 1;
            if (upperBound > Int32.MaxValue)
                throw new ArgumentException(SR.GetString(SR.PageIndexPageSizeBad), "pageIndex " + "and pageSize");
        }

        //http://social.msdn.microsoft.com/forums/en-US/adodotnetentityframework/thread/9d9b04a3-6661-44cc-b36f-39f4c7b03458
        ///<summary>
        ///</summary>
        ///<param name="valueSelector"></param>
        ///<param name="values"></param>
        ///<typeparam name="TElement"></typeparam>
        ///<typeparam name="TValue"></typeparam>
        ///<returns></returns>
        ///<exception cref="ArgumentNullException"></exception>
        public static Expression<Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
            Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
        {
            if (null == valueSelector)
            {
                throw new ArgumentNullException("valueSelector");
            }
            if (null == values)
            {
                throw new ArgumentNullException("values");
            }
            ParameterExpression p = valueSelector.Parameters.Single();
            // p => valueSelector(p) == values[0] || valueSelector(p) == ...
            if (!values.Any())
            {
                return e => false;
            }
            IEnumerable<Expression> equals =
                values.Select(
                    value =>
                    (Expression) Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof (TValue))));
            Expression body = equals.Aggregate((accumulate, equal) => Expression.Or(accumulate, equal));
            return Expression.Lambda<Func<TElement, bool>>(body, p);
        }

        ///<summary>
        ///</summary>
        ///<param name="ctx"></param>
        ///<param name="applicationName"></param>
        ///<returns></returns>
        public static Application GetOrCreateApplication(AspNetDbEntities ctx, string applicationName)
        {
            Application app = ctx.Applications.FirstOrDefault(a => a.LoweredApplicationName == applicationName.ToLower());
            if (app == null)
            {
                app = new Application
                    {ApplicationName = applicationName, LoweredApplicationName = applicationName.ToLower()};
                ctx.AddToApplications(app);
                ctx.SaveChanges();
            }
            return app;
        }

        ///<summary>
        ///</summary>
        ///<returns></returns>
        public static string GetDefaultAppName()
        {
            try
            {
                string appName = HostingEnvironment.ApplicationVirtualPath;
                if (String.IsNullOrEmpty(appName))
                {
                    ProcessModule mainModule = Process.GetCurrentProcess().MainModule;
                    if (mainModule != null)
                    {
                        appName = mainModule.ModuleName;

                        int indexOfDot = appName.IndexOf('.');
                        if (indexOfDot != -1)
                        {
                            appName = appName.Remove(indexOfDot);
                        }
                    }
                }

                if (String.IsNullOrEmpty(appName))
                {
                    return "/";
                }
                return appName;
            }
            catch
            {
                return "/";
            }
        }


        ///<summary>
        ///</summary>
        ///<param name="param"></param>
        ///<param name="checkForNull"></param>
        ///<param name="checkIfEmpty"></param>
        ///<param name="checkForCommas"></param>
        ///<param name="maxSize"></param>
        ///<returns></returns>
        public static bool ValidateParameter(ref string param, bool checkForNull, bool checkIfEmpty,
                                             bool checkForCommas, int maxSize)
        {
            if (param == null)
            {
                return !checkForNull;
            }

            param = param.Trim();
            if ((checkIfEmpty && param.Length < 1) ||
                (maxSize > 0 && param.Length > maxSize) ||
                (checkForCommas && param.Contains(",")))
            {
                return false;
            }

            return true;
        }


        ///<summary>
        ///</summary>
        ///<param name="param"></param>
        ///<param name="checkForNull"></param>
        ///<param name="checkIfEmpty"></param>
        ///<param name="checkForCommas"></param>
        ///<param name="maxSize"></param>
        ///<param name="paramName"></param>
        ///<exception cref="ArgumentNullException"></exception>
        ///<exception cref="ArgumentException"></exception>
        public static void CheckParameter(ref string param, bool checkForNull, bool checkIfEmpty, bool checkForCommas,
                                          int maxSize, string paramName)
        {
            if (param == null)
            {
                if (checkForNull)
                {
                    throw new ArgumentNullException(paramName);
                }

                return;
            }

            param = param.Trim();
            if (checkIfEmpty && param.Length < 1)
            {
                throw new ArgumentException(SR.GetString(SR.ParameterCanNotBeEmpty, paramName), paramName);
            }

            if (maxSize > 0 && param.Length > maxSize)
            {
                throw new ArgumentException(
                    SR.GetString(SR.ParameterTooLong, paramName, maxSize.ToString(CultureInfo.InvariantCulture)),
                    paramName);
            }

            if (checkForCommas && param.Contains(","))
            {
                throw new ArgumentException(SR.GetString(SR.ParameterCanNotContainComma, paramName), paramName);
            }
        }

        ///<summary>
        ///</summary>
        ///<param name="param"></param>
        ///<param name="checkForNull"></param>
        ///<param name="checkIfEmpty"></param>
        ///<param name="checkForCommas"></param>
        ///<param name="maxSize"></param>
        ///<param name="paramName"></param>
        ///<exception cref="ArgumentNullException"></exception>
        ///<exception cref="ArgumentException"></exception>
        public static void CheckArrayParameter(ref string[] param, bool checkForNull, bool checkIfEmpty,
                                               bool checkForCommas, int maxSize, string paramName)
        {
            if (param == null)
            {
                throw new ArgumentNullException(paramName);
            }

            if (param.Length < 1)
            {
                throw new ArgumentException(SR.GetString(SR.ParameterArrayEmpty, paramName), paramName);
            }

            Hashtable values = new Hashtable(param.Length);
            for (int i = param.Length - 1; i >= 0; i--)
            {
                CheckParameter(ref param[i], checkForNull, checkIfEmpty, checkForCommas, maxSize,
                               paramName + "[ " + i.ToString(CultureInfo.InvariantCulture) + " ]");
                if (values.Contains(param[i]))
                {
                    throw new ArgumentException(SR.GetString(SR.ParameterDuplicateArrayElement, paramName), paramName);
                }
                values.Add(param[i], param[i]);
            }
        }

        ///<summary>
        ///</summary>
        ///<param name="config"></param>
        ///<param name="valueName"></param>
        ///<param name="defaultValue"></param>
        ///<returns></returns>
        ///<exception cref="ProviderException"></exception>
        public static bool GetBooleanValue(NameValueCollection config, string valueName, bool defaultValue)
        {
            string sValue = config[valueName];
            if (sValue == null)
            {
                return defaultValue;
            }

            bool result;
            if (bool.TryParse(sValue, out result))
            {
                return result;
            }
            throw new ProviderException(SR.GetString(SR.ValueMustBeBoolean, valueName));
        }

        ///<summary>
        ///</summary>
        ///<param name="config"></param>
        ///<param name="valueName"></param>
        ///<param name="defaultValue"></param>
        ///<param name="zeroAllowed"></param>
        ///<param name="maxValueAllowed"></param>
        ///<returns></returns>
        ///<exception cref="ProviderException"></exception>
        public static int GetIntValue(NameValueCollection config, string valueName, int defaultValue, bool zeroAllowed,
                                      int maxValueAllowed)
        {
            string sValue = config[valueName];

            if (sValue == null)
            {
                return defaultValue;
            }

            int iValue;
            if (!Int32.TryParse(sValue, out iValue))
            {
                if (zeroAllowed)
                {
                    throw new ProviderException(SR.GetString(SR.ValueMustBeNonNegativeInteger, valueName));
                }

                throw new ProviderException(SR.GetString(SR.ValueMustBePositiveInteger, valueName));
            }

            if (zeroAllowed && iValue < 0)
            {
                throw new ProviderException(SR.GetString(SR.ValueMustBeNonNegativeInteger, valueName));
            }

            if (!zeroAllowed && iValue <= 0)
            {
                throw new ProviderException(SR.GetString(SR.ValueMustBePositiveInteger, valueName));
            }

            if (maxValueAllowed > 0 && iValue > maxValueAllowed)
            {
                throw new ProviderException(SR.GetString(SR.ValueTooBig, valueName,
                                                         maxValueAllowed.ToString(CultureInfo.InvariantCulture)));
            }

            return iValue;
        }


        // Returns whether the virtual path is relative.  Note that this returns true for
        // app relative paths (e.g. "~/sub/foo.aspx")
        ///<summary>
        ///</summary>
        ///<param name="virtualPath"></param>
        ///<returns></returns>
        public static bool IsRelativeUrl(string virtualPath)
        {
            // If it has a protocol, it's not relative
            if (virtualPath.IndexOf(":", StringComparison.Ordinal) != -1)
                return false;

            return !IsRooted(virtualPath);
        }

        ///<summary>
        ///</summary>
        ///<param name="basepath"></param>
        ///<returns></returns>
        public static bool IsRooted(String basepath)
        {
            return (String.IsNullOrEmpty(basepath) || basepath[0] == '/' || basepath[0] == '\\');
        }
    }
}